implement terms of service check wizard page
authorJyrki Gadinger <nilsding@nilsding.org>
Mon, 17 Feb 2025 17:01:08 +0000 (18:01 +0100)
committerMatthieu Gallien <matthieu.gallien@nextcloud.com>
Thu, 20 Feb 2025 11:00:54 +0000 (12:00 +0100)
this is only shown if the TOS haven't been accepted yet!

for now this only opens the browser, similar to the Flow2Auth page

the `TermsOfServiceCheckWidget` is a combination of the `Flow2Auth` and
the `Flow2AuthWidget` classes -- in the future we ideally display the
required TOS directly in the wizard

Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
13 files changed:
src/gui/CMakeLists.txt
src/gui/owncloudsetupwizard.cpp
src/gui/owncloudsetupwizard.h
src/gui/wizard/flow2authcredspage.cpp
src/gui/wizard/owncloudhttpcredspage.cpp
src/gui/wizard/owncloudwizard.cpp
src/gui/wizard/owncloudwizard.h
src/gui/wizard/termsofservicecheckwidget.cpp [new file with mode: 0644]
src/gui/wizard/termsofservicecheckwidget.h [new file with mode: 0644]
src/gui/wizard/termsofservicecheckwidget.ui [new file with mode: 0644]
src/gui/wizard/termsofservicewizardpage.cpp
src/gui/wizard/termsofservicewizardpage.h
src/gui/wizard/webviewpage.cpp

index 5bb37c6ce905cc41dd0e3cba712fdc01b173b7d6..d999868509a2a7c3e5eac2ebbfe639e51d4e1367 100644 (file)
@@ -43,6 +43,7 @@ set(client_UI_SRCS
     wizard/owncloudconnectionmethoddialog.ui
     wizard/owncloudhttpcredspage.ui
     wizard/owncloudsetupnocredspage.ui
+    wizard/termsofservicecheckwidget.ui
     wizard/webview.ui
     wizard/welcomepage.ui
 )
@@ -240,6 +241,8 @@ set(client_SRCS
     wizard/flow2authwidget.cpp
     wizard/owncloudsetuppage.h
     wizard/owncloudsetuppage.cpp
+    wizard/termsofservicecheckwidget.h
+    wizard/termsofservicecheckwidget.cpp
     wizard/termsofservicewizardpage.h
     wizard/termsofservicewizardpage.cpp
     wizard/owncloudwizardcommon.h
index 8f10fc52f65c4db7fc51bec82752fc989e23528f..0916ffb738f9a6f5857abea6fa2ebac46efaea47 100644 (file)
@@ -28,7 +28,6 @@
 #include "sslerrordialog.h"
 #include "wizard/owncloudwizard.h"
 #include "wizard/owncloudwizardcommon.h"
-#include "connectionvalidator.h"
 
 #include "creds/credentialsfactory.h"
 #include "creds/abstractcredentials.h"
@@ -382,12 +381,20 @@ void OwncloudSetupWizard::testOwnCloudConnect()
     job->setFollowRedirects(false);
     job->setProperties(QList<QByteArray>() << "getlastmodified");
     connect(job, &PropfindJob::result, _ocWizard, &OwncloudWizard::successfulStep);
-    connect(job, &PropfindJob::finishedWithError, this, [this] (QNetworkReply *reply) {
+    connect(job, &PropfindJob::finishedWithError, this, [this] (QNetworkReply *reply) -> void {
         if (reply && reply->error() == QNetworkReply::ContentAccessDenied) {
-            testTermsOfService();
-        } else {
-            slotAuthError();
+            // A 403 might indicate that the terms of service need to be signed.
+            // catch this special case here, fall back to the standard error handler if it's not TOS-related
+            auto davException = OCC::getExceptionFromReply(reply);
+            if (!davException.first.isEmpty() && davException.first == QStringLiteral(R"(OCA\TermsOfService\TermsNotSignedException)")) {
+                // authentication was successful, but the user hasn't signed the terms of service yet.  Prompt for that in the next step
+                qCInfo(lcWizard) << "Terms of service not accepted yet!  Will prompt the user in the next step";
+                _ocWizard->_needsToAcceptTermsOfService = true;
+                _ocWizard->successfulStep();
+                return;
+            }
         }
+        slotAuthError();
     });
 
     job->start();
@@ -426,34 +433,25 @@ void OwncloudSetupWizard::slotAuthError()
                       "\"%1\". The URL is bad, the server is misconfigured.")
                        .arg(Utility::escape(redirectUrl.toString()));
 
+    } else if (reply->error() == QNetworkReply::ContentNotFoundError) {
         // A 404 is actually a success: we were authorized to know that the folder does
         // not exist. It will be created later...
-    } else if (reply->error() == QNetworkReply::ContentAccessDenied) {
-        testTermsOfService();
-        return;
-    } else if (reply->error() == QNetworkReply::ContentNotFoundError) {
         _ocWizard->successfulStep();
         return;
 
-        // Provide messages for other errors, such as invalid credentials.
     } else if (reply->error() != QNetworkReply::NoError) {
-        auto davException = OCC::getExceptionFromReply(reply);
+        // Provide messages for other errors, such as invalid credentials.
 
         if (!_ocWizard->account()->credentials()->stillValid(reply)) {
             errorMsg = tr("Access forbidden by server. To verify that you have proper access, "
                           "<a href=\"%1\">click here</a> to access the service with your browser.")
                            .arg(Utility::escape(_ocWizard->account()->url().toString()));
-        } else if (!davException.first.isEmpty() && davException.first == QStringLiteral(R"(OCA\TermsOfService\TermsNotSignedException)")) {
-            qCInfo(lcWizard) << "Terms of service not accepted yet!";
-            // TODO: it would be cool to display a new wizard page containing the terms of service
-            errorMsg = tr("Please accept the <a href=\"%1\">Terms of Service</a> with your browser and try again.")
-                           .arg(Utility::escape(_ocWizard->account()->url().toString()));
         } else {
             errorMsg = job->errorStringParsingBody();
         }
 
-        // Something else went wrong, maybe the response was 200 but with invalid data.
     } else {
+        // Something else went wrong, maybe the response was 200 but with invalid data.
         errorMsg = tr("There was an invalid response to an authenticated WebDAV request");
     }
 
@@ -488,14 +486,6 @@ bool OwncloudSetupWizard::checkDowngradeAdvised(QNetworkReply *reply)
     return true;
 }
 
-void OwncloudSetupWizard::testTermsOfService()
-{
-    _termsOfServiceChecker = new TermsOfServiceChecker{_ocWizard->account(), this};
-
-    connect(_termsOfServiceChecker, &TermsOfServiceChecker::done, this, &OwncloudSetupWizard::termsOfServiceChecked);
-    _termsOfServiceChecker->start();
-}
-
 void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString &localFolder, const QString &remoteFolder)
 {
     qCInfo(lcWizard) << "Setup local sync folder for new oC connection " << localFolder;
@@ -749,17 +739,6 @@ void OwncloudSetupWizard::slotSkipFolderConfiguration()
     emit ownCloudWizardDone(QDialog::Accepted);
 }
 
-void OwncloudSetupWizard::termsOfServiceChecked()
-{
-    if (_termsOfServiceChecker && _termsOfServiceChecker->needToSign()) {
-        QDesktopServices::openUrl(_ocWizard->account()->url());
-    } else {
-        _ocWizard->successfulStep();
-        delete _termsOfServiceChecker;
-        _termsOfServiceChecker = nullptr;
-    }
-}
-
 AccountState *OwncloudSetupWizard::applyAccountChanges()
 {
     AccountPtr newAccount = _ocWizard->account();
index fab85d9bbb4fe0a89868eb8d19de7e21e4ec583f..b3ad88a8f9680f98753a0b93c1c03abe67ab6334 100644 (file)
@@ -45,6 +45,7 @@ public:
     /** Run the wizard */
     static void runWizard(QObject *obj, const char *amember, QWidget *parent = nullptr);
     static bool bringWizardToFrontIfVisible();
+
 signals:
     // overall dialog close signal.
     void ownCloudWizardDone(int);
@@ -70,8 +71,6 @@ private slots:
     void slotAssistantFinished(int);
     void slotSkipFolderConfiguration();
 
-    void termsOfServiceChecked();
-
 private:
     explicit OwncloudSetupWizard(QObject *parent = nullptr);
     ~OwncloudSetupWizard() override;
@@ -82,9 +81,7 @@ private:
     bool ensureStartFromScratch(const QString &localFolder);
     AccountState *applyAccountChanges();
     bool checkDowngradeAdvised(QNetworkReply *reply);
-    void testTermsOfService();
 
-    TermsOfServiceChecker *_termsOfServiceChecker = nullptr;
     OwncloudWizard *_ocWizard = nullptr;
     QString _initLocalFolder;
     QString _remoteFolder;
index a2f23f6ef2631b589b70b405d643d279923d7562..8eef42fab0f4be402ab797ae1d74b9a9e123c48e 100644 (file)
@@ -104,7 +104,13 @@ void Flow2AuthCredsPage::slotFlow2AuthResult(Flow2Auth::Result r, const QString
 
 int Flow2AuthCredsPage::nextId() const
 {
-    return WizardCommon::Page_TermsOfService;
+    const auto ocWizard = qobject_cast<OwncloudWizard *>(wizard());
+    Q_ASSERT(ocWizard);
+    if (ocWizard->needsToAcceptTermsOfService()) {
+        return WizardCommon::Page_TermsOfService;
+    }
+
+    return WizardCommon::Page_AdvancedSetup;
 }
 
 void Flow2AuthCredsPage::setConnected()
index ed514b72a459cc420b6d9bbd7c33792abfdd74aa..436b46b3da8713368b8766eaa2522f7603ab926d 100644 (file)
@@ -153,6 +153,12 @@ bool OwncloudHttpCredsPage::validatePage()
 
 int OwncloudHttpCredsPage::nextId() const
 {
+    const auto ocWizard = qobject_cast<OwncloudWizard *>(wizard());
+    Q_ASSERT(ocWizard);
+    if (ocWizard->needsToAcceptTermsOfService()) {
+        return WizardCommon::Page_TermsOfService;
+    }
+
     return WizardCommon::Page_AdvancedSetup;
 }
 
index 4fb364b8f4fb23e5980b2dcc635c731c630df5a1..3a209312bd0b788c4ffdc760405a8892b9628e59 100644 (file)
@@ -54,6 +54,7 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
     , _setupPage(new OwncloudSetupPage(this))
     , _httpCredsPage(new OwncloudHttpCredsPage(this))
     , _flow2CredsPage(new Flow2AuthCredsPage)
+    , _termsOfServicePage(new TermsOfServiceWizardPage)
     , _advancedSetupPage(new OwncloudAdvancedSetupPage(this))
 #ifdef WITH_WEBENGINE
     , _webViewPage(new WebViewPage(this))
@@ -72,6 +73,7 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
     setPage(WizardCommon::Page_ServerSetup, _setupPage);
     setPage(WizardCommon::Page_HttpCreds, _httpCredsPage);
     setPage(WizardCommon::Page_Flow2AuthCreds, _flow2CredsPage);
+    setPage(WizardCommon::Page_TermsOfService, _termsOfServicePage);
     setPage(WizardCommon::Page_AdvancedSetup, _advancedSetupPage);
 #ifdef WITH_WEBENGINE
     if (!useFlow2()) {
@@ -117,6 +119,7 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
     connect(this, &OwncloudWizard::styleChanged, _setupPage, &OwncloudSetupPage::slotStyleChanged);
     connect(this, &OwncloudWizard::styleChanged, _advancedSetupPage, &OwncloudAdvancedSetupPage::slotStyleChanged);
     connect(this, &OwncloudWizard::styleChanged, _flow2CredsPage, &Flow2AuthCredsPage::slotStyleChanged);
+    connect(this, &OwncloudWizard::styleChanged, _termsOfServicePage, &TermsOfServiceWizardPage::styleChanged);
 
     customizeStyle();
 
@@ -217,6 +220,11 @@ bool OwncloudWizard::isConfirmBigFolderChecked() const
     return _advancedSetupPage->isConfirmBigFolderChecked();
 }
 
+bool OwncloudWizard::needsToAcceptTermsOfService() const
+{
+    return _needsToAcceptTermsOfService;
+}
+
 QString OwncloudWizard::ocUrl() const
 {
     QString url = field("OCUrl").toString().simplified();
@@ -263,7 +271,7 @@ void OwncloudWizard::successfulStep()
 #endif // WITH_WEBENGINE
 
     case WizardCommon::Page_TermsOfService:
-        _termsOfServicePage->initializePage();
+        // nothing to do here
         break;
 
     case WizardCommon::Page_AdvancedSetup:
@@ -292,6 +300,9 @@ void OwncloudWizard::slotCustomButtonClicked(const int which)
     } else if (which == WizardButton::CustomButton2) {
         // Because QWizard doesn't have a way of directly going to a specific page (!!!)
         restart();
+
+        // in case the wizard had been cancelled at a page where the need for signing the TOS got checked:
+        _needsToAcceptTermsOfService = false;
     }
 }
 
@@ -336,7 +347,8 @@ void OwncloudWizard::slotCurrentPageChanged(int id)
 #ifdef WITH_WEBENGINE
         id == WizardCommon::Page_WebView ||
 #endif // WITH_WEBENGINE
-        id == WizardCommon::Page_Flow2AuthCreds) {
+        id == WizardCommon::Page_Flow2AuthCreds ||
+        id == WizardCommon::Page_TermsOfService) {
         setButtonLayout({ QWizard::BackButton, QWizard::Stretch });
     } else if (id == WizardCommon::Page_AdvancedSetup) {
         setButtonLayout({ QWizard::CustomButton2, QWizard::Stretch, QWizard::CustomButton1, QWizard::FinishButton });
index 7f950db06493f63ae4ca17c4641d6ed7128d370c..df8e8c6fe10b85afaed5554bb34aa37a9d49fce9 100644 (file)
@@ -69,6 +69,7 @@ public:
     [[nodiscard]] bool useFlow2() const;
     [[nodiscard]] bool useVirtualFileSync() const;
     [[nodiscard]] bool isConfirmBigFolderChecked() const;
+    [[nodiscard]] bool needsToAcceptTermsOfService() const;
 
     void displayError(const QString &, bool retryHTTPonly);
     [[nodiscard]] AbstractCredentials *getCredentials() const;
@@ -140,6 +141,8 @@ private:
 
     bool _useFlow2 = ConfigFile().forceLoginV2();
 
+    bool _needsToAcceptTermsOfService = false;
+
     friend class OwncloudSetupWizard;
 };
 
diff --git a/src/gui/wizard/termsofservicecheckwidget.cpp b/src/gui/wizard/termsofservicecheckwidget.cpp
new file mode 100644 (file)
index 0000000..a5f625b
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) by Jyrki Gadinger <nilsding@nilsding.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "termsofservicecheckwidget.h"
+
+#include "wizard/owncloudwizardcommon.h"
+#include "theme.h"
+#include "configfile.h"
+
+#include "QProgressIndicator.h"
+
+#include <QClipboard>
+#include <QDesktopServices>
+
+namespace OCC {
+
+Q_LOGGING_CATEGORY(lcTosCheckWidget, "nextcloud.gui.wizard.termsofservicecheckwidget", QtInfoMsg)
+
+
+TermsOfServiceCheckWidget::TermsOfServiceCheckWidget(QWidget *parent)
+    : QWidget(parent)
+    , _progressIndicator(new QProgressIndicator(this))
+{
+    _pollTimer.setInterval(1000);
+    QObject::connect(&_pollTimer, &QTimer::timeout, this, &TermsOfServiceCheckWidget::slotPollTimerTimeout);
+
+    _ui.setupUi(this);
+
+    connect(_ui.openLinkButton, &QPushButton::clicked, this, &TermsOfServiceCheckWidget::slotOpenBrowser);
+    connect(_ui.copyLinkButton, &QPushButton::clicked, this, &TermsOfServiceCheckWidget::slotCopyLinkToClipboard);
+
+    auto sizePolicy = _progressIndicator->sizePolicy();
+    sizePolicy.setRetainSizeWhenHidden(true);
+    _progressIndicator->setSizePolicy(sizePolicy);
+
+    _ui.progressLayout->addWidget(_progressIndicator);
+    stopSpinner(false);
+
+    customizeStyle();
+}
+
+TermsOfServiceCheckWidget::~TermsOfServiceCheckWidget() {
+}
+
+void TermsOfServiceCheckWidget::start()
+{
+    ConfigFile cfg;
+    std::chrono::milliseconds polltime = cfg.remotePollInterval();
+    qCInfo(lcTosCheckWidget) << "setting remote poll timer interval to" << polltime.count() << "msec";
+    _secondsInterval = (polltime.count() / 1000);
+    _secondsLeft = _secondsInterval;
+
+    _pollTimer.start();
+    // open browser when the wizard page is shown
+    slotOpenBrowser();
+}
+
+void TermsOfServiceCheckWidget::setUrl(const QUrl &url)
+{
+    _url = url;
+}
+
+void TermsOfServiceCheckWidget::termsNotAcceptedYet()
+{
+    _secondsLeft = _secondsInterval;
+    _isBusy = false;
+    statusChanged(Status::statusPollCountdown);
+}
+
+void TermsOfServiceCheckWidget::setLogo()
+{
+    const auto backgroundColor = palette().window().color();
+    const auto logoIconFileName = Theme::instance()->isBranded() ? Theme::hidpiFileName("external.png", backgroundColor)
+                                                                 : Theme::hidpiFileName(":/client/theme/colored/external.png");
+    _ui.logoLabel->setPixmap(logoIconFileName);
+}
+
+void TermsOfServiceCheckWidget::slotStyleChanged()
+{
+    customizeStyle();
+}
+
+void TermsOfServiceCheckWidget::slotPollTimerTimeout()
+{
+    if (_isBusy) {
+        return;
+    }
+
+    _isBusy = true;
+
+    _secondsLeft--;
+    if (_secondsLeft > 0) {
+        statusChanged(Status::statusPollCountdown);
+        _isBusy = false;
+        return;
+    }
+
+    statusChanged(Status::statusPollNow);
+    Q_EMIT pollNow();
+}
+
+void TermsOfServiceCheckWidget::slotOpenBrowser()
+{
+    QDesktopServices::openUrl(_url);
+}
+
+void TermsOfServiceCheckWidget::slotCopyLinkToClipboard()
+{
+    statusChanged(Status::statusCopyLinkToClipboard);
+    QApplication::clipboard()->setText(_url.toString(QUrl::FullyEncoded));
+}
+
+void TermsOfServiceCheckWidget::statusChanged(Status status)
+{
+    switch (status)
+    {
+    case statusPollCountdown:
+        if (_statusUpdateSkipCount > 0) {
+            _statusUpdateSkipCount--;
+            return;
+        }
+
+        _ui.statusLabel->setText(tr("Waiting for terms to be accepted") + QStringLiteral("… (%1)").arg(_secondsLeft));
+        stopSpinner(true);
+        return;
+
+    case statusPollNow:
+        _statusUpdateSkipCount = 0;
+        _ui.statusLabel->setText(tr("Polling") + QStringLiteral("…"));
+        startSpinner();
+        return;
+
+    case statusCopyLinkToClipboard:
+        _statusUpdateSkipCount = 3;
+        _ui.statusLabel->setText(tr("Link copied to clipboard."));
+        stopSpinner(true);
+        return;
+    }
+}
+
+void TermsOfServiceCheckWidget::startSpinner()
+{
+    _ui.progressLayout->setEnabled(true);
+    _ui.statusLabel->setVisible(true);
+    _progressIndicator->setVisible(true);
+    _progressIndicator->startAnimation();
+
+    _ui.openLinkButton->setEnabled(false);
+    _ui.copyLinkButton->setEnabled(false);
+}
+
+void TermsOfServiceCheckWidget::stopSpinner(bool showStatusLabel)
+{
+    _ui.progressLayout->setEnabled(false);
+    _ui.statusLabel->setVisible(showStatusLabel);
+    _progressIndicator->setVisible(false);
+    _progressIndicator->stopAnimation();
+
+    _ui.openLinkButton->setEnabled(_statusUpdateSkipCount == 0);
+    _ui.copyLinkButton->setEnabled(_statusUpdateSkipCount == 0);
+}
+
+void TermsOfServiceCheckWidget::customizeStyle()
+{
+    setLogo();
+
+    if (_progressIndicator) {
+        const auto isDarkBackground = Theme::isDarkColor(palette().window().color());
+        if (isDarkBackground) {
+            _progressIndicator->setColor(Qt::white);
+        } else {
+            _progressIndicator->setColor(Qt::black);
+        }
+    }
+
+    _ui.openLinkButton->setText(tr("Open Browser"));
+
+    _ui.copyLinkButton->setText(tr("Copy Link"));
+
+    WizardCommon::customizeHintLabel(_ui.statusLabel);
+}
+
+} // namespace OCC
diff --git a/src/gui/wizard/termsofservicecheckwidget.h b/src/gui/wizard/termsofservicecheckwidget.h
new file mode 100644 (file)
index 0000000..1c28149
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) by Jyrki Gadinger <nilsding@nilsding.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#pragma once
+
+#include <QWidget>
+#include <QTimer>
+
+#include "ui_termsofservicecheckwidget.h"
+
+class QProgressIndicator;
+
+namespace OCC {
+
+class TermsOfServiceCheckWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    enum Status {
+        statusPollCountdown = 1,
+        statusPollNow,
+        statusCopyLinkToClipboard,
+    };
+
+    TermsOfServiceCheckWidget(QWidget *parent = nullptr);
+    ~TermsOfServiceCheckWidget() override;
+
+    void start();
+    void setUrl(const QUrl &url);
+    void termsNotAcceptedYet();
+
+public Q_SLOTS:
+    void slotStyleChanged();
+
+Q_SIGNALS:
+    void pollNow();
+
+private Q_SLOTS:
+    void slotPollTimerTimeout();
+    void slotOpenBrowser();
+    void slotCopyLinkToClipboard();
+
+private:
+    Ui_TermsOfServiceCheckWidget _ui{};
+    QTimer _pollTimer;
+    QProgressIndicator *_progressIndicator = nullptr;
+    int _statusUpdateSkipCount = 0;
+    qint64 _secondsLeft = 0LL;
+    qint64 _secondsInterval = 0LL;
+    bool _isBusy = false;
+    QUrl _url;
+
+    void statusChanged(Status status);
+    void startSpinner();
+    void stopSpinner(bool showStatusLabel);
+    void customizeStyle();
+    void setLogo();
+
+};
+
+} // namespace OCC
diff --git a/src/gui/wizard/termsofservicecheckwidget.ui b/src/gui/wizard/termsofservicecheckwidget.ui
new file mode 100644 (file)
index 0000000..be52d44
--- /dev/null
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TermsOfServiceCheckWidget</class>
+ <widget class="QWidget" name="TermsOfServiceCheckWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>597</width>
+    <height>387</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>280</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Terms of Service</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <spacer name="verticalSpacer_6">
+     <property name="orientation">
+      <enum>Qt::Orientation::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="logoLabel">
+       <property name="text">
+        <string>Logo</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Orientation::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>32</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="font">
+        <font>
+         <pointsize>12</pointsize>
+         <bold>true</bold>
+        </font>
+       </property>
+       <property name="text">
+        <string>Switch to your browser to accept the terms of service</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="verticalSpacer_4">
+       <property name="orientation">
+        <enum>Qt::Orientation::Vertical</enum>
+       </property>
+       <property name="sizeType">
+        <enum>QSizePolicy::Policy::Expanding</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>8</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="statusLabel">
+       <property name="text">
+        <string notr="true">Status</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignCenter</set>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="verticalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Orientation::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>32</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="progressLayout"/>
+     </item>
+     <item>
+      <spacer name="verticalSpacer_5">
+       <property name="orientation">
+        <enum>Qt::Orientation::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>64</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QHBoxLayout" name="horizontalLayout_2">
+       <item>
+        <spacer name="horizontalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Orientation::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <widget class="QPushButton" name="copyLinkButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="font">
+            <font>
+             <pointsize>9</pointsize>
+            </font>
+           </property>
+           <property name="text">
+            <string notr="true">copyLinkButton</string>
+           </property>
+           <property name="autoDefault">
+            <bool>true</bool>
+           </property>
+           <property name="default">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="openLinkButton">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="font">
+            <font>
+             <pointsize>9</pointsize>
+            </font>
+           </property>
+           <property name="text">
+            <string notr="true">openLinkButton</string>
+           </property>
+           <property name="autoDefault">
+            <bool>true</bool>
+           </property>
+           <property name="default">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Orientation::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer_3">
+     <property name="orientation">
+      <enum>Qt::Orientation::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Policy::Expanding</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>60</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
index 5a7ec7a7ad9eff2265a67fe7c164515cd3d3695c..f5b577628150c75001897847025a4d3677b6fc18 100644 (file)
@@ -18,6 +18,7 @@
 #include "owncloudsetupwizard.h"
 #include "wizard/owncloudwizard.h"
 #include "wizard/owncloudwizardcommon.h"
+#include "wizard/termsofservicecheckwidget.h"
 #include "connectionvalidator.h"
 
 #include <QVBoxLayout>
@@ -29,14 +30,35 @@ OCC::TermsOfServiceWizardPage::TermsOfServiceWizardPage()
     : QWizardPage()
 {
     _layout = new QVBoxLayout(this);
+
+    _termsOfServiceCheckWidget = new TermsOfServiceCheckWidget;
+    _layout->addWidget(_termsOfServiceCheckWidget);
+
+    connect(this, &TermsOfServiceWizardPage::styleChanged, _termsOfServiceCheckWidget, &TermsOfServiceCheckWidget::slotStyleChanged);
+    connect(_termsOfServiceCheckWidget, &TermsOfServiceCheckWidget::pollNow, this, &TermsOfServiceWizardPage::slotPollNow);
 }
 
 void OCC::TermsOfServiceWizardPage::initializePage()
 {
+    _ocWizard = qobject_cast<OwncloudWizard *>(wizard());
+    Q_ASSERT(_ocWizard);
+
+    _termsOfServiceChecker = new TermsOfServiceChecker{_ocWizard->account(), this};
+    connect(_termsOfServiceChecker, &TermsOfServiceChecker::done, this, &TermsOfServiceWizardPage::termsOfServiceChecked);
+
+    _termsOfServiceCheckWidget->setUrl(_ocWizard->account()->url());
+    _termsOfServiceCheckWidget->slotStyleChanged();
+    _termsOfServiceCheckWidget->start();
+
+    connect(_ocWizard, &OwncloudWizard::onActivate, this, &TermsOfServiceWizardPage::slotPollNow);
 }
 
 void OCC::TermsOfServiceWizardPage::cleanupPage()
 {
+    disconnect(_ocWizard, &OwncloudWizard::onActivate, this, &TermsOfServiceWizardPage::slotPollNow);
+
+    _termsOfServiceChecker->deleteLater();
+    _termsOfServiceChecker = nullptr;
 }
 
 int OCC::TermsOfServiceWizardPage::nextId() const
@@ -51,21 +73,21 @@ bool OCC::TermsOfServiceWizardPage::isComplete() const
 
 void TermsOfServiceWizardPage::slotPollNow()
 {
-    _termsOfServiceChecker = new TermsOfServiceChecker{_ocWizard->account(), this};
+    if (!_termsOfServiceChecker) {
+        return;
+    }
 
-    connect(_termsOfServiceChecker, &TermsOfServiceChecker::done, this, &TermsOfServiceWizardPage::termsOfServiceChecked);
     _termsOfServiceChecker->start();
 }
 
 void TermsOfServiceWizardPage::termsOfServiceChecked()
 {
     if (_termsOfServiceChecker && _termsOfServiceChecker->needToSign()) {
-        QDesktopServices::openUrl(_ocWizard->account()->url());
-    } else {
-        _ocWizard->successfulStep();
-        delete _termsOfServiceChecker;
-        _termsOfServiceChecker = nullptr;
+        _termsOfServiceCheckWidget->termsNotAcceptedYet();
+        return;
     }
+    _ocWizard->successfulStep();
 }
 
 }
+
index 86765e2fb970f268251a20a3aae8615c11823271..2312d98668116e65bb8d30bfa11fa20048c7012e 100644 (file)
@@ -23,6 +23,7 @@ namespace OCC {
 
 class OwncloudWizard;
 class TermsOfServiceChecker;
+class TermsOfServiceCheckWidget;
 
 class TermsOfServiceWizardPage : public QWizardPage
 {
@@ -35,18 +36,21 @@ public:
     [[nodiscard]] int nextId() const override;
     [[nodiscard]] bool isComplete() const override;
 
+public Q_SLOTS:
+    void slotPollNow();
+
 Q_SIGNALS:
-    void connectToOCUrl(const QString &);
     void pollNow();
-
-private Q_SLOTS:
-    void slotPollNow();
-    void termsOfServiceChecked();
+    void styleChanged();
 
 private:
     QVBoxLayout *_layout = nullptr;
     OwncloudWizard *_ocWizard = nullptr;
     TermsOfServiceChecker *_termsOfServiceChecker = nullptr;
+    TermsOfServiceCheckWidget *_termsOfServiceCheckWidget = nullptr;
+
+private Q_SLOTS:
+    void termsOfServiceChecked();
 };
 
 } // namespace OCC
index a3587d1d8e3b07a7ec8fc303b8ce7eb20bd82bf1..0d47cb23ee42acd9dc96804a6a937ef2c2e2bda6 100644 (file)
@@ -92,7 +92,13 @@ void WebViewPage::cleanupPage()
 }
 
 int WebViewPage::nextId() const {
-    return WizardCommon::Page_TermsOfService;
+    const auto ocWizard = qobject_cast<OwncloudWizard *>(wizard());
+    Q_ASSERT(ocWizard);
+    if (ocWizard->needsToAcceptTermsOfService()) {
+        return WizardCommon::Page_TermsOfService;
+    }
+
+    return WizardCommon::Page_AdvancedSetup;
 }
 
 bool WebViewPage::isComplete() const {